package com.leenmvc.core.dao.communal;

import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.dao.communal.dataEntity.DataSourceGroup;
import com.leenmvc.core.enums.SqlType;
import com.leenmvc.core.init.Constant;
import com.leenmvc.core.init.InitDb;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.thread.ThreadLocalManager;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Random;

public interface ConnectionFactory {

    final static Logger logger = LoggerFactory.getLogger(ConnectionFactory.class);

    Random random = new Random();

    /**
     * 获取数据库链接的核心代码
     * 具体的业务场景需要具体定制
     * 规则：
     *
     *
     * @param wrapper
     * @param cls
     * @return
     */
    default Connection getConnection(Wrapper wrapper, Class<?> cls) {
            /* 编码实现数据隔离 */
            try {
                String sql = wrapper.getSql();
                String souceGroup = BaseEntity.getSourceGroup(cls);
                DataSource dataSource = null;
                DataSourceGroup dsGroup = InitDb.getDsGroup(souceGroup);
                String schema = BaseEntity.getSchema(cls);
                if(!StringUtils.isEmpty(schema)) { // 如果指定了schema（库标识）
                    dataSource = dsGroup.getDataSourceFactory(schema).getDataSource();
                    return transConnection(sql, dataSource);
                }

                if (sql.startsWith(Constant.SQL_SELECT) && TransactionManager.getTrans() == 0) { // 随机分配一个读库给到即可(只有非事务状态下才存在从读库群获取数据的情况)
                    int readSourceLength = dsGroup.getReadSources().length;
                    if(readSourceLength > 0) {
                        int next = random.nextInt(readSourceLength);
                        dataSource = dsGroup.getReadSources()[next].getDataSource();
                    }
                } else { // 写库(目前只支持一主多从， 如果是多个写库，会有事务问题)
                    int writeSourceLength = dsGroup.getWriteSources().length;
                    if(writeSourceLength > 0) {
                        int next = random.nextInt(writeSourceLength);
                        dataSource = dsGroup.getWriteSources()[next].getDataSource();
                    }
                }

                // 如果没有找到连接池 那么就使用默认的连接池
                if (dataSource == null) {
                    dataSource = dsGroup.getDefaultSource().getDataSource();
                }

                /* 根据本地线程变量transLocal判断当前线程所处地事务状态是在事务内，还是事务外 */
                Connection connection = transConnection(sql, dataSource);
                return connection;
            } catch (SQLException e) {
                logger.error(e.getMessage(), e);
            }
            return null;
    };

    /**
     * 公用的数据库连接获取方法
     *
     * @param sql
     * @param cls
     * @return
     */
    default Connection getConnection(String sql, Class<?> cls) {
        try {
            String souceGroup = BaseEntity.getSourceGroup(cls);
            DataSource dataSource = null;
            DataSourceGroup dsGroup = InitDb.getDsGroup(souceGroup);
            String schema = BaseEntity.getSchema(cls);
            if(!StringUtils.isEmpty(schema)) { // 如果指定了schema（库标识）
                dataSource = dsGroup.getDataSourceFactory(schema).getDataSource();
                return transConnection(sql, dataSource);
            }

            if (sql.startsWith(Constant.SQL_SELECT)) { // 随机分配一个读库给到即可
                int readSourceLength = dsGroup.getReadSources().length;
                if(readSourceLength > 0) {
                    int next = random.nextInt(readSourceLength);
                    dataSource = dsGroup.getReadSources()[next].getDataSource();
                }
            } else {
                int writeSourceLength = dsGroup.getWriteSources().length;
                if(writeSourceLength > 0) {
                    int next = random.nextInt(writeSourceLength);
                    dataSource = dsGroup.getWriteSources()[next].getDataSource();
                }
            }

            // 如果没有找到连接池 那么就使用默认的连接池
            if (dataSource == null) {
                dataSource = dsGroup.getDefaultSource().getDataSource();
            }

            /* 根据本地线程变量transLocal判断当前线程所处地事务状态是在事务内，还是事务外 */
            return transConnection(sql, dataSource);
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    };

    default SqlSessionFactory getSqlSessionFactory(Class<?> cls) {
        String souceGroup = BaseEntity.getSourceGroup(cls);
        DataSource dataSource = null;
        DataSourceGroup dsGroup = InitDb.getDsGroup(souceGroup);
        String schema = BaseEntity.getSchema(cls);
        if(!StringUtils.isEmpty(schema)) { // 如果指定了schema（库标识）
            return dsGroup.getDataSourceFactory(schema).getSqlSessionFactory();
        }
        return dsGroup.getDefaultSource().getSqlSessionFactory();
    }

    /**
     * 关闭连接
     *
     * @param result
     * @param preparedStatement
     * @param conn
     */
    default void close(ResultSet result, PreparedStatement preparedStatement, Connection conn) {
        try {
            if (result != null) result.close();
            if (preparedStatement != null) preparedStatement.close();
            if (conn != null && TransactionManager.getTrans() == 0) {
                conn.close();
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }
    };

    /**
     * 关闭连接
     *
     * @param result
     * @param preparedStatement
     * @param conn
     * @param sqlType
     */
    default void close(ResultSet result, PreparedStatement preparedStatement, Connection conn, SqlType sqlType) {
        try {
            if (result != null) result.close();
            if (preparedStatement != null) preparedStatement.close();
            if (conn != null && TransactionManager.getTrans() == 0) {
                conn.close();
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }
    };

    /**
     * 根据是否为事务返回不同的连接
     *
     * @param sql
     * @param dataSource
     * @return
     * @throws SQLException
     */
    default Connection transConnection(String sql, DataSource dataSource) throws SQLException {
        if (TransactionManager.getTrans() == 1) {
            Connection transactionConnection = TransactionManager.getConnection(dataSource);
            if (transactionConnection == null) {
                Connection connection = dataSource.getConnection();
                connection.setAutoCommit(false);
                TransactionManager.addConnection(dataSource, connection);
                ThreadLocalManager.put("threadDataSource", dataSource);
                return TransactionManager.getConnection(dataSource);
            } else {
                return transactionConnection;
            }
        }
        long start = System.nanoTime();
        Connection connection = dataSource.getConnection();
        logger.info("获取数据库连接用时：{}", System.nanoTime() - start);
//        PooledDataSource pds = (PooledDataSource)dataSource;
//        logger.info("------------c3p0连接池链接状态--------------");
//        logger.info("c3p0连接池中 【 总共 】 连接数量：" + pds.getNumConnectionsDefaultUser());
//        logger.info("c3p0连接池中 【  忙  】 连接数量：" + pds.getNumBusyConnectionsDefaultUser());
//        logger.info("c3p0连接池中 【 空闲 】 连接数量：" + pds.getNumIdleConnectionsDefaultUser());
//        logger.info("c3p0连接池中 【未关闭】 连接数量：" + pds.getNumUnclosedOrphanedConnectionsAllUsers());
        return connection;
    }
}
